package archer.framework.common.support.exception.solver;

import archer.framework.common.support.exception.view.ExceptionJsonView;
import archer.framework.protocol.exception.BusinessException;
import archer.framework.protocol.exception.NotLoginedException;
import archer.framework.protocol.result.ExecuteResult;
import archer.framework.protocol.result.ResultCode;
import archer.framework.utils.ValidateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Properties;

/**
 * 全局异常处理
 *
 * @author kent
 * @date 16/6/15.
 */
public class CustomExceptionHandlerExceptionSolver extends ExceptionHandlerExceptionResolver {

    private static final Logger LOGGER = LoggerFactory.getLogger(CustomExceptionHandlerExceptionSolver.class);

    private static final String DEFAULT_ERROR_VIEW = "/error/500";

    //登录url
    private String logoutUrl;

    //默认错误页面
    private String defaultErrorView;

    //异常->错误页面映射
    private Properties exceptionMappings;


    @Override
    protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod, Exception exception) {

        ResponseBody responseBody = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), ResponseBody.class);

        if (responseBody != null) {
            return jsonView(exception);
        }

        RestController restController = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), RestController.class);
        if (restController != null) {
            return jsonView(exception);
        }

        return errorView(exception);
    }


    //------------------------------------ private ----------------------------------------

    /**
     * 返回json类型错误数据
     *
     * @return
     */
    private ModelAndView jsonView(Exception exception) {

        if (!(exception instanceof BusinessException)) {
            logException(exception);
        }
        return new ModelAndView(new ExceptionJsonView()).addObject(ExceptionJsonView.EXECUTE_RESULT, exceptionToExecuteResult(exception));
    }

    /**
     * 返回根据异常定制的错误页面
     *
     * @return
     */
    private ModelAndView errorView(Exception exception) {

        logException(exception);

        // 未登录跳转到登录页面
        if (exception instanceof NotLoginedException) {
            return new ModelAndView("redirect:" + logoutUrl);
        }

        // 其它未定义异常由异常处理器处理
        if (exceptionMappings != null) {
            for (Object key : exceptionMappings.keySet()) {
                if (exception.getClass().getName().equals(key)) {
                    return new ModelAndView(exceptionMappings.getProperty(String.valueOf(key)))
                            .addObject(ExceptionJsonView.EXECUTE_RESULT, exceptionToExecuteResult(exception));
                }
            }
        }

        return defaultErrorView(exception);
    }

    // 默认为系统内部异常
    private ExecuteResult exceptionToExecuteResult(Exception exception) {

        // 业务异常转化为ExecuteResult
        if (exception instanceof BusinessException) {
            BusinessException businessException = (BusinessException) exception;
            return businessException.toExecuteResult();
        }

        // 不是自定义业务异常的都按系统内部异常处理
        return new ExecuteResult(ResultCode.INTERNAL_ERROR);
    }

    private ModelAndView defaultErrorView(Exception exception) {

        defaultErrorView = ValidateUtils.isEmpty(defaultErrorView) ? DEFAULT_ERROR_VIEW : defaultErrorView;

        return new ModelAndView(defaultErrorView)
                .addObject(ExceptionJsonView.EXECUTE_RESULT, exceptionToExecuteResult(exception));
    }

    private void logException(Exception e) {

        LOGGER.info(e.getMessage(), e);
    }


    //-------------------------------- getter setter -----------------------------------
    public String getDefaultErrorView() {
        return defaultErrorView;
    }

    public CustomExceptionHandlerExceptionSolver setDefaultErrorView(String defaultErrorView) {
        this.defaultErrorView = defaultErrorView;
        return this;
    }

    public Properties getExceptionMappings() {
        return exceptionMappings;
    }

    public CustomExceptionHandlerExceptionSolver setExceptionMappings(Properties exceptionMappings) {
        this.exceptionMappings = exceptionMappings;
        return this;
    }

    public String getLogoutUrl() {
        return logoutUrl;
    }

    public CustomExceptionHandlerExceptionSolver setLogoutUrl(String logoutUrl) {
        this.logoutUrl = logoutUrl;
        return this;
    }
}
